package im.composer.midi;

import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Transmitter;

public class MidiCable implements Transmitter,Receiver{

	private Transmitter transmitter;
	private Receiver receiver;
	
	public MidiCable() {
	}

	public MidiCable(Transmitter transmitter, Receiver receiver) {
		this.transmitter = transmitter;
		this.receiver = receiver;
	}
	
	public MidiCable(String name, Receiver receiver) throws MidiUnavailableException{
		this.receiver = receiver;
		connectInput(name);
	}
	
	public MidiCable(Transmitter transmitter,String name) throws MidiUnavailableException{
		this.transmitter = transmitter;
		connectOutput(name);
	}
	
	public MidiCable(String transmitter, String receiver) throws MidiUnavailableException{
		connectInput(transmitter);
		connectOutput(receiver);
	}

	public static MidiDevice lookForDevice(String name, boolean canInput, boolean canOutput) {
		MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
		for (MidiDevice.Info i : infos) {
			if (i.getName().equals(name) || i.getDescription().equals(name)) {
				try {
					MidiDevice dev = MidiSystem.getMidiDevice(i);
					if (canInput && dev.getMaxTransmitters() == 0) {
						continue;
					}
					if (canOutput && dev.getMaxReceivers() == 0) {
						continue;
					}
					return dev;
				} catch (MidiUnavailableException e) {
				}
			}
		}
		return null;
	}
	
	public void connectInput(MidiDevice dev) throws MidiUnavailableException{
		if(!dev.isOpen()){
			dev.open();
		}
		transmitter = dev.getTransmitter();
		transmitter.setReceiver(this);
	}
	
	public void connectInput(String name) throws MidiUnavailableException{
		connectInput(lookForDevice(name,true,false));
	}
	
	public void connectOutput(MidiDevice dev) throws MidiUnavailableException{
		if(!dev.isOpen()){
			dev.open();
		}
		Receiver rcv = dev.getReceiver();
		setReceiver(rcv);
	}

	public void connectOutput(String name) throws MidiUnavailableException{
		connectOutput(lookForDevice(name,false,true));
	}
	
	@Override
	public void send(MidiMessage message, long timeStamp) {
		receiver.send(message, timeStamp);
	}

	@Override
	public void setReceiver(Receiver receiver) {
		this.receiver = receiver;
	}

	@Override
	public Receiver getReceiver() {
		return receiver;
	}

	@Override
	public void close() {
		try {
			transmitter.close();
		} catch (Exception e) {
		}
		try {
			receiver.close();
		} catch (Exception e) {
		}
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((receiver == null) ? 0 : receiver.hashCode());
		result = prime * result + ((transmitter == null) ? 0 : transmitter.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		MidiCable other = (MidiCable) obj;
		if (receiver == null) {
			if (other.receiver != null)
				return false;
		} else if (!receiver.equals(other.receiver))
			return false;
		if (transmitter == null) {
			if (other.transmitter != null)
				return false;
		} else if (!transmitter.equals(other.transmitter))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "MidiCable [transmitter=" + transmitter + ", receiver=" + receiver + "]";
	}
}
